#include "GCore.h"
#include "GModuleManager.h"
#include "GTimeManager.h"
#include "GMessageManager.h"
#include "GInputManager.h"
#include "GApplication.h"

#include "GTaskManager.h"
#include "GGameManager.h"
#include "GInputModule.h"

#include "GError.h"

#include "GTask.h"

namespace GEngine
{
	/************************************************/
	/*				GCoreUpdateTask					*/
	/************************************************/

	class GCoreUpdateTask : public GTask
	{
	private:
		float		ElapsedTime;

	public:
					GCoreUpdateTask(GTaskResult* TaskResult, GTaskPriority TaskPriority, bool TaskThreadSafe);
					~GCoreUpdateTask();

		GUInt32		Execute(GSize WorkerId);
	};

	GCoreUpdateTask::GCoreUpdateTask(GTaskResult* TaskResult, GTaskPriority TaskPriority, bool TaskThreadSafe)
		: GTask(TaskResult, TaskPriority, TaskThreadSafe)
	{
	}

	GCoreUpdateTask::~GCoreUpdateTask()
	{	
	}

	GUInt32 GCoreUpdateTask::Execute(GSize WorkerId)
	{
		//! component/manager update order
		//! 1)  Message
		//! 2)  Input
		//! 3)  Time
		//! 4)  Scene / Game
		//! 5)  Application
		//! 6)  AI
		//! 7)  Physic
		//! 8)  Sound
		//! 9)  Animation
		//! 10) Cull
		//! 11) Render
		//! 12) Network

		float ElapsedTime = (float)::mGTimeManager->GetUpdateTime();

		::mGMessageManager->Update();
		::mGInputManager->Update(ElapsedTime);
		::mGTimeManager->Update();
		::mGGameManager->Update(ElapsedTime);
		::mGAppModule->Update(ElapsedTime);

		return 0;
	}


	/************************************************/
	/*					GCore						*/
	/************************************************/

	GCore::GCore()
	{
		AppModule = NULL;
		InputModule = NULL;
		SoundModule = NULL;
		PhysicModule = NULL;
		GraphicModule = NULL;
		NetworkModule = NULL;

		ExitSwitch.store(false, std::memory_order_release);
	}

	GCore::~GCore()
	{
	}

	void GCore::MainLoop()
	{
		do
		{
			// Create root task
			GTaskResult RootResult;
			GCoreUpdateTask RootTask(&RootResult, G_TP_DEFAULT, false);

			// schedule and wait for result
			mGTaskManager->ScheduleRoot(&RootTask);
			mGTaskManager->BusyWait(&RootResult);
		}
		while (!ExitSwitch.load(std::memory_order_acquire));
	}

	GApplication* GCore::GetAppModule() const
	{
		return AppModule;
	}

	GInputModule* GCore::GetInputModule() const
	{
		return InputModule;
	}

	GSoundModule* GCore::GetSoundModule() const
	{
		return SoundModule;
	}

	GPhysicModule* GCore::GetPhysicModule() const
	{
		return PhysicModule;
	}

	GGraphicModule* GCore::GetGraphicModule() const
	{
		return GraphicModule;
	}

	GNetworkModule* GCore::GetNetworkModule() const
	{
		return NetworkModule;
	}

	void GCore::Run()
	{
		MainLoop();
	}

	void GCore::Exit()
	{
		ExitSwitch.store(true, std::memory_order_release);
	}

	bool GCore::CreateAppModule()
	{
		const char* ModuleName = ::mGModuleManager->GetImplementation("GEngine::GApplication", 0);
		GModule* Module = ::mGModuleManager->CreateModule(ModuleName);

		GUtil::GError::DebugCheck(Module == NULL, "Cannot create application module instance.");

		AppModule = static_cast<GApplication*>(Module);

		return true;
	}

	bool GCore::CreateInputModule()
	{
		return false;
	}

	bool GCore::CreateSoundModule()
	{
		return false;
	}

	bool GCore::CreatePhysicModule()
	{
		return false;
	}

	bool GCore::CreateGraphicModule()
	{
		return false;
	}

	bool GCore::CreateNetworkModule()
	{
		return false;
	}

	bool GCore::Initialize() OVERRIDE
	{
		if (!GInitializable::Initialize())
		{
			return false;
		}

		bool Result = false;

		// Initialize GWindow statically
		Result = GWindow::StaticInitialize();
		GUtil::GError::DebugCheck(!Result, "GWindow failed to initialize static!");

		// Initialize managers
		Result = ::mGModuleManager->Initialize();
		GUtil::GError::DebugCheck(!Result, "Module manager failed to initialize!");
		
		Result = ::mGTaskManager->Initialize();
		GUtil::GError::DebugCheck(!Result, "Task manager failed to initialize!");

		Result = ::mGMessageManager->Initialize();
		GUtil::GError::DebugCheck(!Result, "Message manager failed to initialize!");

		Result = ::mGInputManager->Initialize();
		GUtil::GError::DebugCheck(!Result, "Input manager failed to initialize!");

		Result = ::mGTimeManager->Initialize();
		GUtil::GError::DebugCheck(!Result, "Time manager failed to initialize!");

		Result = ::mGGameManager->Initialize();
		GUtil::GError::DebugCheck(!Result, "Game manager failed to initialize!");

		// Create modules
		CreateAppModule();

		// Initialize modules
		Result = ::mGAppModule->Initialize();
		GUtil::GError::DebugCheck(!Result, "Application module failed to initialize!");
		
		return true;
	}

	bool GCore::InitializeLate() OVERRIDE
	{
		if (!GInitializable::InitializeLate())
		{
			return false;
		}

		bool Result = false;

		Result = ::mGModuleManager->InitializeLate();
		GUtil::GError::DebugCheck(!Result, "Module manager failed to initialize late!");

		Result = ::mGMessageManager->InitializeLate();
		GUtil::GError::DebugCheck(!Result, "Message manager failed to initialize late!");

		Result = ::mGInputManager->InitializeLate();
		GUtil::GError::DebugCheck(!Result, "Input manager failed to initialize late!");

		Result = ::mGTimeManager->InitializeLate();
		GUtil::GError::DebugCheck(!Result, "Time manager failed to initialize late!");

		Result = ::mGGameManager->InitializeLate();
		GUtil::GError::DebugCheck(!Result, "Game manager failed to initialize late!");

		Result = ::mGTaskManager->InitializeLate();
		GUtil::GError::DebugCheck(!Result, "Task manager failed to initialize late!");

		Result = ::mGAppModule->InitializeLate();
		GUtil::GError::DebugCheck(!Result, "Application module failed to initialize late!");

		return true;
	}

	void GCore::Uninitialize() OVERRIDE
	{
		::mGAppModule->Uninitialize();
		::mGModuleManager->Uninitialize();
		::mGMessageManager->Uninitialize();
		::mGInputManager->Uninitialize();
		::mGTimeManager->Uninitialize();
		::mGGameManager->Uninitialize();
		::mGTaskManager->Uninitialize();

		G_DELETE(AppModule)

		// Uninitialize GWindow
		GWindow::StaticUninitialize();

		GInitializable::Uninitialize();
	}

	void GCore::UninitializeEarly() OVERRIDE
	{
		::mGAppModule->UninitializeEarly();
		::mGModuleManager->UninitializeEarly();
		::mGMessageManager->UninitializeEarly();
		::mGInputManager->UninitializeEarly();
		::mGTimeManager->UninitializeEarly();
		::mGGameManager->UninitializeEarly();
		::mGTaskManager->UninitializeEarly();

		GInitializable::UninitializeEarly();
	}

}